Skip to content

Add theme variants and stabilize markdown rendering - #1212

Open
felipeggv wants to merge 5 commits into
RunMaestro:rcfrom
felipeggv:build/indigo-deep-wine-red-themes
Open

Add theme variants and stabilize markdown rendering#1212
felipeggv wants to merge 5 commits into
RunMaestro:rcfrom
felipeggv:build/indigo-deep-wine-red-themes

Conversation

@felipeggv

@felipeggv felipeggv commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add Indigo Blue, Deep Wine Red, Mustard, and Deep Purple theme variants.
  • Keep composer text visible during mention highlighting.
  • Cache Shiki highlighted HTML and sanitized Mermaid SVGs to avoid remount/loading flicker in rendered markdown.

Validation

  • git diff --check
  • npm test -- CodeFence.test.tsx MermaidRenderer.test.tsx
  • npm run lint
  • npx eslint src/renderer/components/CodeFence/CodeFence.tsx src/renderer/components/MermaidRenderer.tsx --no-warn-ignored
  • npx prettier --check src/renderer/components/CodeFence/CodeFence.tsx src/renderer/components/MermaidRenderer.tsx src/__tests__/renderer/components/CodeFence.test.tsx src/__tests__/renderer/components/MermaidRenderer.test.tsx
  • Pre-push validate:push: bun run format:check:all && bun run lint && bun run lint:eslint && bun run test
  • Full Vitest result from pre-push: 1363 test files passed, 1 skipped; 33,935 tests passed, 108 skipped

Summary by CodeRabbit

  • New Features
    • Added four additional built-in themes, including new dark options (Indigo Blue and Deep Wine Red) with full color configurations.
  • Improvements
    • Code highlighting now reuses cached results to reduce visual flicker on remount.
    • Mention overlay behavior is more robust: it hides during active text selection and keeps scrolling in sync.
    • Textarea resizing now respects the element’s computed max-height.
    • Mermaid rendering is memoized per chart/theme for more stable, faster updates.
  • Tests
    • Expanded theme ID coverage and added regression tests for caching and overlay behavior.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds two built-in dark themes, expands theme validation, caches CodeFence and Mermaid output, improves mention-overlay selection and scroll synchronization, and clamps textarea sizing to computed CSS limits.

Changes

Theme expansion

Layer / File(s) Summary
Theme contract and registry
src/shared/theme-types.ts, src/shared/themes.ts
Adds four theme IDs to the ThemeId union and validation list, and registers complete dark-theme color definitions for indigo-blue and deep-wine-red.
Theme validation updates
src/__tests__/renderer/constants/themes.test.ts, src/__tests__/shared/theme-types.test.ts
Updates the theme count expectation to 24 and expands the valid-ID fixture.

Renderer caching

Layer / File(s) Summary
CodeFence highlight cache
src/renderer/components/CodeFence/CodeFence.tsx, src/__tests__/renderer/components/CodeFence.test.tsx
Adds bounded Shiki HTML caching and reuses highlighted output across remounts with identical inputs.
Mermaid SVG cache
src/renderer/components/MermaidRenderer.tsx, src/__tests__/renderer/components/MermaidRenderer.test.tsx
Adds bounded theme-aware SVG caching, stable theme tracking, counter-based diagram IDs, and coverage for equivalent-theme rerenders and remounts.

Input overlay and sizing

Layer / File(s) Summary
Mention overlay selection behavior
src/renderer/components/InputArea/components/InputTextarea.tsx, src/__tests__/renderer/components/InputArea.test.tsx
Tracks text selection, controls overlay visibility and layering, synchronizes initial scroll position, and expands interaction tests.
CSS-aware textarea sizing
src/renderer/components/InputArea/utils/textareaSizing.ts, src/__tests__/renderer/components/InputArea/utils/textareaSizing.test.ts
Clamps resizing to the textarea’s computed CSS maxHeight and tests the resulting height.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: reachrazamair

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title captures the two main changes: new theme variants and markdown rendering stabilization.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds two built-in dark themes. The main changes are:

  • Indigo Blue and Deep Wine Red theme definitions.
  • New theme IDs in the shared type and runtime validator.
  • Updated theme count and valid-ID test coverage.

Confidence Score: 5/5

This looks safe to merge after a small visual cleanup.

  • The shared registry, type guard, and tests were updated together.
  • The only noted issue is a dark accent color that can make active UI states hard to see.

src/shared/themes.ts

Important Files Changed

Filename Overview
src/shared/themes.ts Adds two dark theme definitions with all required color keys. The Indigo Blue accent may be too dark for active-state visibility.
src/shared/theme-types.ts Adds both new IDs to the ThemeId union and runtime isValidThemeId guard.
src/tests/renderer/constants/themes.test.ts Updates the expected theme count for the expanded registry.
src/tests/shared/theme-types.test.ts Adds the new IDs to valid theme sample coverage.

Reviews (1): Last reviewed commit: "feat(themes): add Deep Wine Red theme" | Re-trigger Greptile

Comment thread src/shared/themes.ts
border: '#06070a',
textMain: '#f2ebc0',
textDim: '#c7c2b3',
accent: '#1f2f63',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Indigo Accent Nearly Disappears

When the Indigo Blue theme is selected, accent is used for active theme UI like scrollbar highlights, but #1f2f63 is very close to the near-black bgMain value #010204. Highlighted controls can become hard to see in this theme, especially where accent is the only active-state indicator.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@felipeggv felipeggv changed the title Add Indigo Blue and Deep Wine Red themes Add ultra dark theme variants Jul 12, 2026
@pedramamini

Copy link
Copy Markdown
Collaborator

@felipeggv Thanks for the contribution! Four new ultra dark variants is a nice addition, and the mechanics here are clean: you updated the ThemeId union, the isValidThemeId runtime allowlist, the THEMES registry, and both test files together, so the three-way sync stays intact. CI is green across all four checks and there are no merge conflicts. Structurally these follow the olive-nights precedent closely (including the rgba(..., 1) form for accentDim), which is exactly right.

One substantive issue before this can go in, and it's an extension of the point Greptile raised on indigo-blue.

accent contrast is below the visibility threshold on all four themes

Greptile flagged the Indigo accent specifically, but the problem generalizes to the whole set. The accent value is very close in luminance to bgMain in each new theme:

Theme accent bgMain accent vs bgMain
olive-nights (existing precedent) #5b675b #0a0b0a 3.32:1
indigo-blue #1f2f63 #010204 1.63:1
deep-wine-red #631f30 #040102 1.75:1
deep-purple #4f2673 #030106 1.84:1
yellow-dark-mustard #6b5314 #040301 2.82:1

WCAG 1.4.11 asks for 3:1 for non-text UI indicators. olive-nights, the closest existing theme in spirit, clears it at 3.32:1. All four new themes fall short.

This matters more than a typical palette nit because of where accent lands. src/renderer/hooks/ui/useThemeStyles.ts:80 maps the theme's accent straight to --accent-color, and the keyboard focus ring falls back to exactly that variable:

/* src/renderer/index.css:329 */
.focus-ring:focus-visible {
	outline: 2px solid var(--focus-ring-color, var(--accent-color, rgba(99, 102, 241, 0.9)));
	outline-offset: 2px;
}

Maestro is a keyboard-first app, so a focus ring at 1.6:1 against a near-black background is effectively invisible. Users tabbing through the UI on these themes would lose track of where focus is.

Worth noting your text colors are all in great shape (textMain and textDim are 11:1 and up against bgMain, and accentForeground on accent is comfortable everywhere). It's specifically accent that needs lifting.

Here are hue-preserving values that clear 3:1, if useful as a starting point. These just scale luminance and keep your color identity intact:

Theme current suggested new ratio
indigo-blue #1f2f63 #3a58b9 3.24:1
deep-wine-red #631f30 #a73451 3.22:1
yellow-dark-mustard #6b5314 #765b16 3.22:1
deep-purple #4f2673 #7e3db8 3.21:1

Feel free to pick your own values, these are only illustrative. The ask is just that accent reaches roughly 3:1 against bgMain so focus and active states stay visible. If you'd rather keep the accents deliberately deep for the aesthetic, the other option is to set an explicit brighter --focus-ring-color for these themes, but bumping accent is the simpler path and keeps the rest of the accent-driven UI (highlights, left borders) legible too.

Minor heads-up, no action needed

Both bots reviewed at commit 3ca0f7c ("Deep Wine Red"), before 990f1ad added the mustard and deep purple variants. That's why their summaries only mention two themes and four IDs. Your diff does correctly define all four, so nothing is actually missing, but the last commit went unreviewed by the bots. That's part of why I checked the full set above.

Once the accent contrast is addressed I'm happy to approve. Thanks again!

@felipeggv felipeggv changed the title Add ultra dark theme variants Add theme variants and stabilize markdown rendering Jul 12, 2026
@pedramamini

Copy link
Copy Markdown
Collaborator

Thanks! Picked this up in a PR sweep but it conflicts with rc right now. Mind rebasing on the latest rc? I will review and merge once it is mergeable.

@jSydorowicz21

Copy link
Copy Markdown
Contributor

Two Markdown issues remain after the current changes:

  1. On an uncached Mermaid update, MermaidRenderer.tsx:383 sets loading but leaves the previous svgContent in state. The loading branch at line 504 only renders when there is no SVG, so diagram A stays visible with no updating state while diagram B renders. I reproduced this in Electron with a delayed real Mermaid render. Please clear the old SVG on a cache miss, or show an explicit updating state, and add a delayed rerender regression.

  2. The new cache limits bound entry count, not memory. Each CodeFence key retains the complete code block plus its highlighted HTML, and each Mermaid entry retains the complete chart plus SVG for the renderer lifetime. A small number of large transcript blocks can therefore retain substantial memory after unmount. Please use a byte-budgeted LRU or skip entries above a conservative input/output size.

I did not repeat the existing contrast and rebase feedback.

@pedramamini

Copy link
Copy Markdown
Collaborator

@felipeggv Thanks again for sticking with this one - the caching work here is still the most valuable part of the PR, and nothing in rc has superseded it. Picking it up in another sweep, so here is a consolidated status of everything outstanding, plus some rebase guidance that should save you real time.

1. Needs a rebase on latest rc (blocking)

rc has moved a lot in the last month and all 11 changed files now conflict:

src/shared/themes.ts
src/shared/theme-types.ts
src/renderer/components/CodeFence/CodeFence.tsx
src/renderer/components/MermaidRenderer.tsx
src/renderer/components/InputArea/components/InputTextarea.tsx
src/renderer/components/InputArea/utils/textareaSizing.ts
+ the 5 corresponding test files

Three things worth knowing before you start, because they change what the resolution should look like:

Drop indigo-blue from this PR. It already landed on rc via #1207, and the definition there is byte-for-byte identical to yours (I diffed the block). Take rc's side wholesale for that theme and keep only deep-wine-red, yellow-dark-mustard, and deep-purple. That removes the bulk of the themes.ts / theme-types.ts conflict.

The theme count test works out unchanged. rc currently expects 21. Adding your remaining three lands on 24, which is exactly what this PR already sets, so src/__tests__/renderer/constants/themes.test.ts needs no further edit once indigo-blue is dropped.

The composer changes need a real re-think, not a mechanical merge. rc has since landed #1169, #1294, #1321, #1329, and #1330, all reworking caret visibility, mention-highlight alignment, and composer selection state in InputTextarea.tsx. Your overlay/selection changes may be partly or wholly superseded. Please re-derive that piece against current rc rather than resolving conflict markers, and drop anything already fixed upstream.

2. Cache entries are written on every streamed chunk

This one sharpens the memory point @jSydorowicz21 raised, and I think it is the most actionable item in the PR.

The highlight effect in CodeFence.tsx depends on the raw code prop, not debouncedCode:

}, [resolvedLang, code, theme.mode]);

Detection is correctly debounced ("streaming doesn't re-detect per char"), but caching is not. So while a fence streams in, every intermediate snapshot calls rememberHighlightedHtml(...) and inserts a distinct entry keyed on the full partial code string. A single 300-line block arriving in ~200 chunks churns ~200 entries through an 80-entry cache, each retaining a progressively longer code string as the key plus its rendered HTML as the value.

Two consequences: the cache thrashes itself during exactly the scenario it was added for, and the retained bytes are far larger than the 80-entry bound suggests. Gating writes on the settled/debounced value, or skipping entries above a conservative size, would fix both at once.

Related and cheap to fix: both caches are FIFO, not LRU. There is no reinsert on a cache hit, so keys().next().value always evicts the oldest insertion regardless of how hot it is. A frequently revisited block can be evicted by cold one-offs. A delete + set on hit makes it a true LRU.

3. Stale Mermaid diagram on a cache miss

Confirmed @jSydorowicz21's first point by reading the diff. The old setSvgContent(null) was removed from the miss path, and the loading gate is now isLoading && !svgContent, so on a cache miss the previous diagram stays on screen with no indication that a new one is rendering.

I read this as intentional anti-flicker, and I do not think reverting to a spinner is right - that would undo the PR's purpose. But silently showing diagram A while B renders is misleading. A subtle updating affordance (reduced opacity, a small spinner in the corner) keeps the anti-flicker win without the lie. A delayed-rerender regression test would be good to have alongside it.

4. Accent contrast: withdrawing this as a blocker

I raised this in my first comment. Since then indigo-blue shipped in #1207 with accent: #1f2f63 unchanged, so the project has effectively set precedent against my own objection and it would be unfair to hold this PR to a stricter bar. Consider it optional. The remaining three accents are still deep enough that the focus ring will be hard to see on them, so if you want to lift them toward ~3:1 against bgMain I would welcome it, but it is your call and I will not block on it.

Summary

Items 1 through 3 are what I would like addressed. Item 1 is the hard blocker since I cannot merge a conflicting branch. If the rebase turns out to be more trouble than it is worth, splitting the CodeFence and Mermaid caching into its own PR against fresh rc would be very welcome - that part is genuinely useful, has no competing implementation upstream, and would avoid the composer churn entirely. Happy to review it quickly either way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants